A sparse verdict already tells the caller to use a screenshot as visual truth,
which made that screenshot the guaranteed next command on every unreadable
screen — a second round trip to obey advice we authored. The user-facing
`snapshot` dispatch now takes the shot itself and links the path in its
warnings.
The fallback is deliberately hung off `dispatchSnapshotViaRuntime` and skipped
for internal observations: selector resolution, settle, and wait polling reach
`captureSnapshot` directly, so a wait polling an unreadable screen cannot turn
into a screenshot per poll. A failed shot is swallowed — the verdict's own
warning still carries the manual remedy, so the fallback can never fail the
snapshot that was asked for.
Sparse captures also say when the screen is the app's problem. Only the
`sparse-tree` reason code is evidence about the app: every backend reached the
screen and it published no semantic content, which is the same emptiness
assistive tech gets. `ax-rejected`, `budget`, `no-nodes` and `capture-failed`
are limits of this tool and stay unattributed, so readers are not sent to file
bugs against code that is not broken.
Summary
A user-facing sparse
snapshotnow captures the screenshot its recovery guidance already requires and returns the client-visible path asfallbackScreenshotPath. The image uses the normal artifact channel, so local and remote clients receive a usable path; internal observations and healthy snapshots do not trigger screenshots.Only a
sparse-treeverdict adds the app accessibility warning. Capture-limit verdicts remain attributed to the tool. The public contract, SDK normalization, digest projection, text output, architecture context, and snapshot documentation all describe the same response.Scope: 16 files in the snapshot response/public projection. No command-family expansion. The
agent-deviceskill remains unchanged because command behavior belongs in versioned help and product documentation.Validation
pnpm check:affected --run— passed all 7 selected gates; 263 test files and 2,240 tests passed.expected 1 screenshot call, received 0.fallbackScreenshotPath, and closed the isolated session cleanly.Residual: the repository has no live app fixture that deliberately publishes a sparse accessibility tree, so the sparse-device branch is proven through the production-route regression rather than a live sparse capture.